home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 201 / 201.xpi / modules / utils.jsm < prev    next >
Text File  |  2010-01-11  |  5KB  |  239 lines

  1. /* You may find the license in the LICENSE file */
  2.  
  3. const EXPORTED_SYMBOLS = [
  4.     'atos',
  5.     'newUUIDString',
  6.     'range',
  7.     'hexdigest',
  8.     'merge',
  9.     'clone',
  10.     'formatNumber',
  11.     'formatTimeDelta',
  12.     'getTimestamp',
  13.     'naturalSort',
  14. ];
  15.  
  16. const Cc = Components.classes;
  17. const Ci = Components.interfaces;
  18. const Cr = Components.results;
  19.  
  20.  
  21.  
  22. /**
  23.  * returns a new UUID in string representation
  24.  * @return String UUID
  25.  * @author Nils
  26.  */
  27. function newUUIDString() {
  28.     let uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
  29.     
  30.     newUUIDString = function() {
  31.         return uuidgen.generateUUID().toString();
  32.     }
  33.     return newUUIDString();
  34. }
  35.  
  36. /**
  37.  * Range generator (python style). Difference: step direction is initialized accordingly if corresponding parameter is omitted.
  38.  * @param start Optional. Start value (default: 0)
  39.  * @param stop Stop value (exclusive)
  40.  * @param step Optional. Step value (default: 1/-1)
  41.  * @author Nils
  42.  */
  43. function range() {
  44.     if (arguments.length == 0) {
  45.         throw Components.results.NS_ERROR_INVALID_ARG;
  46.     }
  47.     let start = 0, stop = new Number(arguments[0]), step;
  48.     if (arguments.length >= 2) {
  49.         start = stop;
  50.         stop = new Number(arguments[1]);
  51.     }
  52.     if (arguments.length >= 3) {
  53.         step = new Number(arguments[2]);
  54.     }
  55.     else {
  56.         step = stop - start > 0 ? 1 : -1; 
  57.     }
  58.     if (!isFinite(start) || !isFinite(stop) || !isFinite(step) || step == 0) {
  59.         throw Cr.NS_ERROR_INVALID_ARG;
  60.     }
  61.     if ((stop - start) / step < 0) {
  62.         // negative range
  63.         return;
  64.     }
  65.     stop += -Math.abs(step) / step;
  66.     stop += step - ((stop - start) % step);
  67.     for (; start != stop; start += step) {
  68.         yield start;
  69.     }
  70.  
  71. }
  72.  
  73. /**
  74.  * Builds the hexdigest of (binary) data
  75.  * @param {Object} data
  76.  * @return {String} hexdigest
  77.  */
  78. function hexdigest(data) {
  79.     data = data.toString();
  80.     return [('0' + data.charCodeAt(i).toString(16)).slice(-2) for (i in range(data.length))].join('');    
  81. }
  82.  
  83. /**
  84.  * Merges the enumeratable properties of two objects   
  85.  * @param {Object} me Object that has the properties added the properties
  86.  * @param {Object} that Object of which the properties are taken
  87.  */
  88. function merge(me, that) {
  89.     for (let c in that) {
  90.         me[c] = that[c];
  91.     }
  92. }
  93.  
  94. /**
  95.  * (Almost) Clones an object. Not instanceof safe :p
  96.  * @param {Object} obj
  97.  * @return {Object} Copy of obj
  98.  */
  99. function clone(obj) {
  100.     var rv = {};
  101.     merge(rv, obj);
  102.     merge(rv.prototype, this.prototype);
  103.     rv.constructor = this.constructor;
  104.     return rv;
  105. }
  106.  
  107. /**
  108.  * Cast non-strings to strings (using toSource if required instead of toString()
  109.  * @param {Object} data
  110.  */
  111. function atos(data) {
  112.     if (typeof(data) == 'string') {
  113.         return data;
  114.     }
  115.     if (data instanceof String || typeof(data) == 'object') {
  116.         try {
  117.             return data.toSource();
  118.         }
  119.         catch (ex) {
  120.             // fall-trough
  121.         }
  122.     }
  123.     return data.toString();
  124. }
  125.  
  126. /**
  127.  * Head-Pads a number so that at it contains least "digits" digits.
  128.  * @param {Object} num The number in question
  129.  * @param {Object} digits Number of digits the results must contain at least
  130.  */
  131. function formatNumber(num, digits) {
  132.     let rv = atos(num);
  133.     digits = Number(digits);
  134.     if (!isFinite(digits)) {
  135.         digits = 3;
  136.     }
  137.     for (let i = rv.length; i < digits; ++i) {
  138.         rv = '0' + rv;
  139.     }
  140.     return rv;
  141. }
  142.  
  143. /**
  144.  * Formats a time delta (seconds)
  145.  * @param {Number} delta in seconds
  146.  * @return {String} formatted result
  147.  */
  148. function formatTimeDelta(delta) {
  149.     let rv = (delta < 0) ? '-' : '';
  150.  
  151.     delta = Math.abs(delta);
  152.     let h = Math.floor(delta / 3600);
  153.     let m = Math.floor((delta % 3600) / 60);
  154.     let s = Math.floor(delta % 60);
  155.     
  156.     if (h) {
  157.         rv += formatNumber(h, 2) + ':';
  158.     }
  159.     return rv + formatNumber(m, 2) + ':' + formatNumber(s, 2);
  160. }
  161.  
  162. /**
  163.  * Converts a Datestring into an integer timestamp.
  164.  * @param {Object} str Datestring or null for current time.
  165.  */
  166. function getTimestamp(str) {
  167.     if (!str) {
  168.         return Date.now();
  169.     }
  170.     let rv = Date.parse(atos(str));
  171.     if (!isFinite(rv)) {
  172.         throw new Error('invalid date');
  173.     }
  174.     return rv;
  175. }
  176.  
  177. function naturalSort(arr, mapper) {
  178.     if (typeof mapper != 'function' && !(mapper instanceof Function)) {
  179.         mapper = function(e) e;
  180.     }
  181.     let isDigit = function(a, i) {
  182.         i = a[i];
  183.         return i >= '0' && i <= '9';
  184.     };
  185.     let compare = function(a, b) {
  186.         return a === b ? 0 : (a < b ? -1 : 1);
  187.     }
  188.     arr = arr.map(
  189.         function(b) {
  190.             let e = mapper(b);
  191.             if (e == null || e == undefined || typeof e == 'number') {
  192.                 return {elem: b, chunks: [e]};
  193.             }
  194.             let a = e.toString().replace(/\b(?:a|one|the)\b/g, ' ').replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ').toLowerCase();
  195.             let len = a.length;
  196.             if (!len) {
  197.                 return {elem: b, chunks: [a]};
  198.             }
  199.             let rv = [];
  200.             let last = isDigit(a, 0);
  201.             let cur = last;
  202.             start = 0;
  203.         
  204.             for (let i = 0; i < len; ++i) {
  205.                 cur = isDigit(a, i);
  206.                 if (cur != last) {
  207.                     rv.push(cur ? a.substr(start, i - start) : Number(a.substr(start, i - start)));
  208.                     start = i;
  209.                     last = cur;
  210.                 }
  211.             }
  212.             if (!rv.length || len - start != 1) {
  213.                 rv.push(cur ? Number(a.substr(start)) : a.substr(start));
  214.             }
  215.             return {elem: b, chunks: rv};
  216.         }
  217.     );
  218.     arr.sort(
  219.         function (a, b) {
  220.             let ai, bi;
  221.             [a, b] = [a.chunks, b.chunks];
  222.             let m = Math.max(a.length, b.length);
  223.             for (let i = 0; i < m; ++i) {
  224.                 let ai = a[i], bi = b[i];
  225.                 let rv = compare(typeof ai, typeof bi);
  226.                 if (rv) {
  227.                     return rv;
  228.                 }
  229.                 rv = compare(ai, bi);
  230.                 if (rv) {
  231.                     return rv;
  232.                 }
  233.             }
  234.             return a.length - b.length;
  235.         }
  236.     );
  237.     return arr.map(function(a) a.elem);
  238. }
  239.